#include<stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "My_sig.h"
struct Msg_sig{
    long type;
    int signal;
};
struct User{
    long type;
    char username[32];
    char password[32];
};
struct Contact{
    long type;
    char name[16];
    char sex[8];
    int age;
    char telephone[16];
    char address[32];
};
//向后台发送数据
void send_data(int msgid,void* data,size_t size){

    msgsnd(msgid,data,size,0);
}
//接受后台的数据
int receive_data(int msgid,void* data,size_t size,long type){
    msgrcv(msgid,data,size,type,0);
    if(type == 9){
        struct Msg_sig* tmp = data;
        printf("%d\n",tmp->signal);
    }
    
    if(errno == ENOMSG){
        return -1;
    }
    return 0;
}

void showmenu(){
    printf("***************************\n");
    printf("***** 1、添加联系人 *****\n");
    printf("***** 2、显示联系人 *****\n");
    printf("***** 3、删除联系人 *****\n");
    printf("***** 4、查找联系人 *****\n");
    printf("***** 5、修改联系人 *****\n");
    printf("***** 6、清空联系人 *****\n");
    printf("***** 0、退出通讯录 *****\n");
    printf("***************************\n");
}

void scanf_contact(struct Contact* contact){
    printf("请输入姓名:\n");
    scanf("%s",contact->name);
    printf("请输入年龄:\n");
    scanf("%d",&contact->age);
    printf("请输入性别(男,女):\n");
    scanf("%s",contact->sex);
    printf("请输入电话号码:\n");
    scanf("%s",contact->telephone);
    printf("请输入家庭住址:\n");
    scanf("%s",contact->address);
}

//注册
int regist(int msgid){
    struct User user = {0};
    printf("请输入注册的用户名:\n");
    scanf("%s",user.username);
    printf("请输入注册的密码:\n");
    scanf("%s",user.password);
    //发送注册信息
    user.type = REGIST;
    send_data(msgid,&user,sizeof(user)-sizeof(long));
    //读取回馈
    struct Msg_sig sig;
    receive_data(msgid,&sig,sizeof(sig)-sizeof(long),RECEIVE_SIG_INT);
    if(sig.signal == OK){
        printf("注册成功!\n");
    }
    else{
        printf("注册失败,请重新注册!\n");
    }
}
void handle_contacts(int msgid);
//登录
int login(int msgid){
    struct User user = {0};
    printf("请输入登录的用户名:\n");
    scanf("%s",user.username);
    printf("请输入登录的密码:\n");
    scanf("%s",user.password);
    //发送登录信息
    user.type = LOGIN;
    send_data(msgid,&user,sizeof(user)-sizeof(long));
    //读取回馈
    struct Msg_sig sig;

    int ret = msgrcv(msgid,&sig,sizeof(sig)-sizeof(long),RECEIVE_SIG_INT,0);
    if (ret == -1) {
        switch (errno) {
            case EAGAIN:
                printf("No message of type %ld\n", RECEIVE_SIG_INT);
                break;
            case E2BIG:
                printf("Message too large for buffer size\n");
                break;
            case EIDRM:
                printf("Message queue removed\n");
                break;
            case EINTR:
                printf("Call was interrupted by a signal\n");
                break;
            case EINVAL:
                printf("Invalid message queue identifier, message size, or message type\n");
                break;
            case ENOMSG:
                printf("No message of type %ld on queue and IPC_NOWAIT flag set\n", RECEIVE_SIG_INT);
                break;
            default:
                printf("Unknown error: %d\n", errno);
        }
    }
    if(sig.signal == OK){
        printf("登录成功!\n");
        //进入二级菜单
        handle_contacts(msgid);
    }
    else{
        printf("用户名或密码错误,请重新登录!\n");
    }
}

//获取所有联系人信息
void getall(int msgid){
    struct Msg_sig sig;
    struct Contact contact;
    sig.signal = 1;
    sig.type = SIG_INT;
    send_data(msgid,&sig,sizeof(sig)-sizeof(long));
    while(1){
        //接受后台发送的数据
        int ret = msgrcv(msgid,&contact,sizeof(contact)-sizeof(long),10,MSG_NOERROR);
        if(contact.age == NO || ret == NO){
            //printf("通讯录暂无联系人\n");
            break;
        }
        printf("姓名:%s   性别:%s  年龄:%d  电话号码:%s  家庭住址:%s\n"
        ,contact.name,contact.sex,contact.age,contact.telephone,contact.address);
    }
}

//添加联系人
void add(int msgid){
    struct Contact contact;
    scanf_contact(&contact);
    contact.type = ADD;
    send_data(msgid,&contact,sizeof(contact)-sizeof(long));
    struct Msg_sig sig;
    receive_data(msgid,&sig,sizeof(int),RECEIVE_SIG_INT);
    if(sig.signal == NO){
        printf("添加失败!\n");
    }
    else {
        printf("添加成功!\n");
    }
}
//查找联系人
int search(int msgid,struct Contact* contact){

    printf("请输入查找的姓名:\n");
    scanf("%s",contact->name);
    contact->type = SEARCH;
    int size = sizeof(struct Contact)-sizeof(long);
    send_data(msgid,contact,size);
    printf("ok!\n");
    receive_data(msgid,contact,size,RECEIVE_CONTACT);
    if(contact->age == -1){
        printf("通讯录中无此联系人!\n");
        return -1;
    }
    return 0;
}
//删除联系人
int delete(int msgid){
    struct Contact contact;
    if(search(msgid,&contact) == NO){
        return NO;
    }

    contact.type = DELETE;
    send_data(msgid,&contact,sizeof(contact)-sizeof(long));
    struct Msg_sig sig;
    receive_data(msgid,&sig,sizeof(int),RECEIVE_SIG_INT);
    if(sig.signal == -1){
        printf("删除失败!\n");
        return -1;
    }
    else{
        printf("删除成功!\n");
    }
    return 0;
}

//修改联系人
void update(int msgid){
    struct Contact contact;
    if(search(msgid,&contact) == -1){
        return ;
    }
    scanf_contact(&contact);
    contact.type = UPDATE;
    send_data(msgid,&contact,sizeof(contact)-sizeof(long));
    struct Msg_sig sig;
    receive_data(msgid,&sig,sizeof(int),RECEIVE_SIG_INT);
    if(sig.signal == 0){
        printf("修改成功!\n");
    }
    else{
        printf("修改失败!\n");
    }
}

//清空联系人
void clear(int msgid){
    struct Msg_sig sig;
    sig.type = SIG_INT;
    sig.signal = 2; 
    //发送清空信号
    send_data(msgid,&sig,sizeof(int));

    receive_data(msgid,&sig,sizeof(int),RECEIVE_SIG_INT);
    if(sig.signal == 0){
        printf("清空成功!\n");
    }
    else{
        printf("清空失败!\n");
    }
}
//处理联系人
void handle_contacts(int msgid){
    while(1){
        showmenu();
        struct Contact contact;
        int op = 0,ret = 0;
        scanf("%d",&op);
        switch (op)
        {
        case 1:
            //添加联系人
            add(msgid);
            break;
        case 2:
            //查看联系人信息
            getall(msgid);
            break;
        case 3:
            //删除联系人
            delete(msgid);
            break;
        case 4:
            //查找联系人
            if(search(msgid,&contact) == 0){
                printf("姓名:%s  性别:%s  年龄:%d  电话号码:%s  家庭住址:%s\n"
                ,contact.name,contact.sex,contact.age,contact.telephone,contact.address);
    
            }
            break;
        case 5:
            //修改联系人
            update(msgid);
            break;
        case 6:
            //清空联系人
            clear(msgid);
            break;
        default:
            break;
        }
        if(op == 0){
            printf("已退出登录\n");
            return;
        }
        printf("输入回车继续.............\n");
        getchar();
        getchar();
    }
}
int main(int argc, char const *argv[])
{
    //用消息队列传递操作信号
    //1.生成key
    int key = ftok("./app2",'b');
    if(key<0){
        perror("ftok err");
        return -1;
    }
    //2.创建或打开消息队列
    int msgid = msgget(key,IPC_CREAT|IPC_EXCL|0666);
    if(msgid<0){
        //消息队列存在
        if(errno == EEXIST){
            msgid = msgget(key,0666);
        }
        else{
            perror("msgget err");
            return -1;
        }
    }
    printf("消息队列创建成功!\n");
    int op = 0;
    while(1){
        //注册,登录
        printf("****************************\n");
        printf("(1) 注册\n");
        printf("(2) 登录\n");
        printf("(3) 退出系统\n");
        printf("****************************\n");
        scanf("%d",&op);
        struct User user = {0}; 
        int ret = -1;
        switch (op)
        {
        case 1:
            regist(msgid);
            break;
        case 2:
            login(msgid);
            break;
        default:
            break;
        }
        if(op == 3){
            //向后台发送退出信号
            struct Msg_sig sig = {SIG_INT,-1};
            send_data(msgid,&sig,sizeof(int));

            printf("欢迎下次使用!\n");
            break;
        }
    }
    return 0;
}
